package com.osce.face.service;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.csce.domain.entity.face.FaceList;
import com.csce.domain.entity.face.FaceUserListDTO;
import com.osce.common.utils.http.*;
import com.csce.domain.entity.face.ao.SlabEnteringFaceAO;
import com.csce.domain.entity.face.ao.SlabEnteringFingerprintAO;
import com.doudou.arc.ArcFaceEngine;
import com.doudou.arc.CompareFaceTask;
import com.doudou.arc.FaceRecognitionResDTO;
import com.doudou.core.container.ListUtil;
import com.doudou.core.container.MapUtil;
import com.doudou.core.lang.ObjUtil;
import com.osce.common.constant.CacheConstants;
import com.osce.common.constant.ScheduleConstants;
import com.osce.common.core.entity.AjaxResult;
import com.osce.common.core.redis.RedisCache;
import com.osce.common.utils.DateUtils;
import com.osce.face.mapper.FaceListMapper;
import com.osce.interfaces.face.IFaceListService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ExecutorCompletionService;

/**
 * 人脸数据Service业务层处理
 *
 * @author zzx
 * @since 2023-11-21
 */
@Service
public class FaceListServiceImpl extends ServiceImpl<FaceListMapper, FaceList> implements IFaceListService {
    private static final Log log = LogFactory.get(IFaceListService.class);

    @Resource private ArcFaceEngine arcFaceEngine;
    @Resource private RedisCache cache;

    // 远程服务
    @Resource private FaceRemoteServiceProcessor faceRemoteServiceProcessor;

    /**
     * 查询人脸数据
     *
     * @param id 人脸数据主键
     * @return 人脸数据
     */
    @Override
    public FaceList selectFaceListById(Long id) {
        return baseMapper.selectFaceListById(id);
    }

    /**
     * 查询人脸数据列表
     *
     * @param faceList 人脸数据
     * @return 人脸数据
     */
    @Override
    public List<FaceList> selectFaceListList(FaceList faceList) {
        return baseMapper.selectFaceListList(faceList);
    }

    @Override
    public List<FaceUserListDTO> selectFaceList(FaceUserListDTO dto) {
        return baseMapper.selectFaceList(dto);
    }

    @Override
    public List<FaceList> selectFaceListAll() {
        return baseMapper.selectFaceListAll();
    }

    /**
     * 新增人脸数据
     *
     * @param faceList 人脸数据
     * @return 结果
     */
    @Override
    public int insertFaceList(FaceList faceList) {
        faceList.setCreateTime(DateUtils.getNowDate());
        int i = baseMapper.insertFaceList(faceList);
        if (i > 0) {
            updateCache(faceList.getId(), faceList.getFaceFeature());
        }
        return i;
    }

    @Override
    public int insertFaceListByFile(byte[] file, Long userId) {
        byte[] extract = arcFaceEngine.extract(file);
        if (extract == null) {
            return 0;
        }
        String faceFeature = arcFaceEngine.featureByteToStr(extract);
        return insertFaceList(FaceList.builder().id(userId).faceFeature(faceFeature).build());
    }

    @Override
    public int insertFaceListByBase64(String base64, Long userId) {
        byte[] extract = arcFaceEngine.extract(base64);
        if (extract == null) {
            return 0;
        }
        String faceFeature = arcFaceEngine.featureByteToStr(extract);
        return insertFaceList(FaceList.builder().id(userId).faceFeature(faceFeature).build());
    }

    /**
     * 修改人脸数据
     *
     * @param faceList 人脸数据
     * @return 结果
     */
    @Override
    public int updateFaceList(FaceList faceList) {
        faceList.setUpdateTime(DateUtils.getNowDate());
        int i = baseMapper.updateFaceList(faceList);
        if (i > 0) {
            updateCache(faceList.getId(), faceList.getFaceFeature());
        }
        return i;
    }

    @Override
    public int updateFaceListByFile(byte[] file, Long userId) {
        byte[] extract = arcFaceEngine.extract(file);
        if (extract == null) {
            return 0;
        }
        String faceFeature = arcFaceEngine.featureByteToStr(extract);
        return updateFaceList(FaceList.builder().id(userId).faceFeature(faceFeature).build());
    }

    @Override
    public int updateFaceListByBase64(String base64, Long userId) {
        byte[] extract = arcFaceEngine.extract(base64);
        if (extract == null) {
            return 0;
        }
        String faceFeature = arcFaceEngine.featureByteToStr(extract);
        return updateFaceList(FaceList.builder().id(userId).faceFeature(faceFeature).build());
    }

    /**
     * 批量删除人脸数据
     *
     * @param ids 需要删除的人脸数据主键
     * @return 结果
     */
    @Override
    public int deleteFaceListByIds(Long[] ids) {
        int i = baseMapper.deleteFaceListByIds(ids);
        if (i > 0) {
            removeCache(ids);
        }
        return i;
    }

    /**
     * 删除人脸数据信息
     *
     * @param id 人脸数据主键
     * @return 结果
     */
    @Override
    public int deleteFaceListById(Long id) {
        int i = baseMapper.deleteFaceListById(id);
        if (i > 0) {
            removeCache(id);
        }
        return i;
    }

    //-----------------------------------------

    /**
     * 人脸比较
     *
     * @param faceFeature 人脸特征值
     */
    @Override
    public AjaxResult faceCompare(String faceFeature) {
        return faceCompare(faceFeature,cache.getCacheMap(CacheConstants.CACHE_FACE_MAP_KEY));
    }
    @Override
    public AjaxResult faceCompare(String faceFeature,List<Long> userIds) {
        Map<String, String> faceMap = MapUtil.hashMap();
        if (ListUtil.isNotEmpty(userIds)){
            for (Long userId : userIds) {
                Object sourceFaceFeature = cache.getCacheMapValue(CacheConstants.CACHE_FACE_MAP_KEY, userId + "");
                faceMap.put(userId + "", sourceFaceFeature + "");
            }
        }
        return faceCompare(faceFeature,faceMap);
    }
    @Override
    public AjaxResult faceCompare(String faceFeature,Map<String, String> faceMap) {
        //使用队列保证线程执行完成
        try {
            ExecutorCompletionService<FaceRecognitionResDTO<String>> completionService = new ExecutorCompletionService<>(CacheConstants.FACE_EXECUTOR_POOL);
            faceMap.forEach((id,sourceFaceFeature)->{
                completionService.submit(new CompareFaceTask<>(faceFeature,sourceFaceFeature,id,arcFaceEngine));
            });
            //获取结果
            List<FaceRecognitionResDTO<String>> resultFaceInfoList = ListUtil.list(false);
            for (int i = 0; i < faceMap.size(); i++) {
                FaceRecognitionResDTO<String> faceRecognitionResDto = completionService.take().get();
                if (ObjUtil.isNotNull(faceRecognitionResDto)) {
                    resultFaceInfoList.add(faceRecognitionResDto);
                }
            }
            if (ListUtil.isNotEmpty(resultFaceInfoList)) {
                //从大到小排序
                resultFaceInfoList.sort(Comparator.comparing(FaceRecognitionResDTO::getSimilar));
                long userId = Long.parseLong(ListUtil.getFirst(resultFaceInfoList).getData());
                return AjaxResult.success("人脸识别成功", userId);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return AjaxResult.error("人脸识别失败");
    }

    /**
     * 人脸比较
     *
     * @param base64 图片base64加密字符串
     */
    @Override
    public AjaxResult faceCompareBase64(String base64) {
        byte[] extract = arcFaceEngine.extract(base64);
        if (extract == null) {
            return AjaxResult.error("未检测到人脸");
        }
        return faceCompare(arcFaceEngine.featureByteToStr(extract));
    }

    /**
     * 人脸比较
     *
     * @param fileBytes 图片字节数组
     */
    @Override
    public AjaxResult faceCompareFileBytes(byte[] fileBytes) {
        byte[] extract = arcFaceEngine.extract(fileBytes);
        if (extract == null) {
            return AjaxResult.error("未检测到人脸");
        }
        return faceCompare(arcFaceEngine.featureByteToStr(extract));
    }


//    @PostConstruct
//    @Scheduled(cron = ScheduleConstants.FACE_CACHE_CRON)
//    public void faceCache() {
//        log.info("===========开始缓存人脸数据============");
//        cache.deleteObject(CacheConstants.CACHE_FACE_MAP_KEY);
//        List<FaceList> faceList = baseMapper.selectFaceListAll();
//        Map<String, String> faces = MapUtil.hashMap();
//        for (FaceList face : faceList) {
//            faces.put(face.getId().toString(), face.getFaceFeature());
//        }
//        cache.setCacheMap(CacheConstants.CACHE_FACE_MAP_KEY, faces);
//    }

    public void updateCache(Long id, String faceFeature) {
        cache.setCacheMapValue(CacheConstants.CACHE_FACE_MAP_KEY, id.toString(), faceFeature);
    }

    public void removeCache(Long... ids) {
        String[] strs = new String[ids.length];
        for (int i = 0; i < ids.length; i++) {
            strs[i] = ids[i].toString();
        }
        cache.deleteCacheMapValue(CacheConstants.CACHE_FACE_MAP_KEY, strs);
    }

    /**
     * 修改传入URL获取人脸数据
     */
    @Override
    public int editImage(ImageAO ao) {
        String image = ao.getImageUrl();
        // 将图片转换为base64字符串
        String base64String = convertImageToBase64(image);
        ao.setImage(base64String);
        // 调用远程服务添加人脸数据
        boolean success = faceRemoteServiceProcessor.callFaceAddService( ao.getUserId(),ao.getImage(),ao. getImageUrl());
        if (!success) {
            log.error("调用远程服务添加人脸数据失败");
            return 0;
        }
        return 1;
    }


    /**
     * 新增传入URL获取人脸数据
     */
    @Override
    public int addImage(String image, Integer userId) {
        System.out.println("图片路径" + image);
        return 1;
    }


    @Override
    public CompareFaceRes callFaceCompareFacesRecognitionService(CompareFaceRecognitionAO ao) {
        return (CompareFaceRes) faceRemoteServiceProcessor.callFaceCompareFacesForgeryService(ao.getImage());
    }


    /**
     * 平板新增人脸数据
     */
    @Override
    public int slabFaceAdd(SlabEnteringFaceAO ao) {
        log.info("===========平板新增人脸数据============");
        // 将图片已经为base64字符串
        ao.setImage(ao.getImageBase64());
        // 调用远程服务添加人脸数据
        boolean success = faceRemoteServiceProcessor.callFaceAddService(ao.getUserId(),ao.getImage(),ao.getImageBase64());
        if (!success) {
            log.error("调用远程服务添加人脸数据失败");
            return 0;
        }
        return 1;
    }

    /**
     * 平板新增指纹数据
     */
    @Override
    public int slabFingerprintAdd(SlabEnteringFingerprintAO ao) {

        log.info("===========平板新增指纹数据============");
        log.info("指纹数据"+ao.getFingerprint());
        return 1;
    }


    /**
     * 将图片文件转换为base64字符串
     * @param imageUrl 图片URL或本地路径
     * @return base64字符串
     */
    private String convertImageToBase64(String imageUrl) {
        try {
            byte[] imageBytes;

            if (imageUrl.startsWith("http")) {
                // 处理网络图片URL
                URL url = new URL(imageUrl);
                try (InputStream inputStream = url.openStream()) {
                    imageBytes = readInputStream(inputStream);
                }
            } else {
                // 处理本地文件路径
                FileInputStream fileInputStream = new FileInputStream(imageUrl);
                try {
                    imageBytes = readInputStream(fileInputStream);
                } finally {
                    fileInputStream.close();
                }
            }

            // 转换为base64
            return Base64.getEncoder().encodeToString(imageBytes);
        } catch (IOException e) {
            log.error("图片转换base64失败", e);
            return null;
        }
    }

    /**
     * 读取输入流并返回字节数组
     * @param inputStream 输入流
     * @return 字节数组
     * @throws IOException IO异常
     */
    private byte[] readInputStream(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length;
        while ((length = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, length);
        }
        return outputStream.toByteArray();
    }





}
